//
// Created by glyn on 2024/3/15.
//
#pragma once

#include "streamdomainsocket.hpp"
#include <thread>

template <uint16_t BUFFER_SIZE = AS_DOMAIN_BUFFER_SIZE>
class StreamDomainServer : public BaseDomainSocket
{
public:
    // Event Listeners:
    std::function<void(StreamDomainSocket<BUFFER_SIZE>*)> onNewConnection = [](StreamDomainSocket<BUFFER_SIZE>* sock){FDR_UNUSED(sock)};

    explicit StreamDomainServer(FDR_ON_ERROR): BaseDomainSocket(onError, SocketType::TCP,SocketCMC::LOCALM)
    {
        int opt = 1;
        setsockopt(this->sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(int));
        setsockopt(this->sock,SOL_SOCKET,SO_REUSEPORT,&opt,sizeof(int));
    }



    // Start listening incoming connections.
    void Listen(FDR_ON_ERROR)
    {
        if (listen(this->sock, 20) == -1)
        {
            onError(errno, "Error: Server can't listen the socket.");
            return;
        }

        std::thread t(Accept, this, onError);
        t.detach();
    }

private:
    static void Accept(StreamDomainServer<BUFFER_SIZE>* server, FDR_ON_ERROR)
    {
        sockaddr_un newSocketInfo;
        socklen_t newSocketInfoLength = sizeof(newSocketInfo);

        int newSocketFileDescriptor = -1;
        while (true)
        {
            newSocketFileDescriptor = accept(server->sock, (sockaddr *)&newSocketInfo, &newSocketInfoLength);
            if (newSocketFileDescriptor == -1)
            {
                if (errno == EBADF || errno == EINVAL) return;

                onError(errno, "Error while accepting a new connection.");

                return;
            }

            StreamDomainSocket<BUFFER_SIZE>* newSocket = new StreamDomainSocket<BUFFER_SIZE>(onError, newSocketFileDescriptor);
            newSocket->deleteAfterClosed = true;

            newSocket->setAddressStruct(newSocketInfo);

            server->onNewConnection(newSocket);
            newSocket->Listen();
        }
    }
};


